/*
 * ============================================================================
 *
 *       Filename:  my_io.c
 *
 *    Description:  net work and file io
 *
 *        Version:  1.0
 *        Created:  2017年03月27日 22时09分42秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  yangkun (yk)
 *          Email:  xyyangkun@163.com
 *        Company:  yangkun.com
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/tcp.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <sys/time.h> 
#include <linux/ip.h>

#ifdef MODULE
#undef MODULE
#endif
#define MODULE "IO"
#include "dbg.h"

/*****************************************************************************
 * @brief	写入socket以前，检查下这个连接是否有效，要不可能造成异常断开时，
			有一个数据包没有写入的情况
 * @param[IN] sockfd socket file descript
 * return =0 socket connected 1 invild socket
 *****************************************************************************/
int check_socket_connected(int sockfd)  
{  
	struct tcp_info info;  
	int len = sizeof(info);  
	if (sockfd <= 0)
	{  
		return 1;  
	}  
	memset(&info,0,sizeof(info));  
	getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);  
	if(info.tcpi_state== 1) 
	{  
		/// printf("socket connected\n");  
		return 0;  
	} 
	else 
	{  
		/// printf("socket disconnected\n");  
		return 1;  
	}  
}  
/*****************************************************************************
 * @brief	 create tcp server
 * @param[IN] ip 点分十进制的字符串ip地址 INADDR_ANY
 * @param[IN] port 端口号
 * return >0 file descript <=0 error
 *****************************************************************************/
int my_create_tcp_server(char *ip, short port)
{
	struct sockaddr_in servaddr;
	int listenfd;
	int ret;

	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	if (listenfd < 0)
	{
		dbg("ERROR in create socket:%s\n", strerror(errno));
		return listenfd;
	}

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr(ip);
	servaddr.sin_port = htons(port);

	int opt = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	if (ret != 0)
	{
		dbg("ERROR in bind:%s\n", strerror(errno));
		return ret;
	}

	ret = listen(listenfd, 20);
	if (ret != 0)
	{
		dbg("ERROR in listen:%s\n", strerror(errno));
		return ret;
	}
	return listenfd;
}

/*****************************************************************************
 * @brief	 create tcp client
 * @param[IN] ip ip str
 * @param[IN] port port num
 * return >0 file descript <=0 error
 *****************************************************************************/
int my_create_tcp_client(char *srvip, short srvport)
{
	struct sockaddr_in servaddr;
	int sockfd, n;
	int ret;
    
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		dbg("ERROR in create socket:%s\n", strerror(errno));
		return sockfd;
	}

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, srvip, &servaddr.sin_addr);
	servaddr.sin_port = htons(srvport);
    
	ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));	
	if (ret != 0)
	{
		dbg("ERROR in connect socket:%s\n", strerror(errno));
		return ret;
	}
	return sockfd;
}

/*****************************************************************************
 * @brief	write data
 * @param[IN] fd write data fd
 * @param[IN]] buf the data point will write
 * @param[IN] size write data size
 * return szie write size or error 
 *****************************************************************************/
int my_write(int fd, char *buf, unsigned int size)
{
	if (0 != check_socket_connected(fd))
	{
		dbg("ERROR in check_socket_connected:%s\n", strerror(errno));
		return -1;
	}
	return write(fd, buf, size);
}

/*****************************************************************************
 * @brief	read data
 * @param[IN] fd read data fd
 * @param[IN]] buf the data point will read
 * @param[IN] size read data size
 * return the read size or error
 *****************************************************************************/
int my_read(int fd, char *buf, unsigned int size)
{
	return read(fd, buf, size);
}

/*****************************************************************************
 * @brief	accept data
 * return the read size or error
 *****************************************************************************/
int my_accept(int listenfd, struct sockaddr_in *cliaddr, socklen_t *cliaddr_len)
{
	return  accept(listenfd, (struct sockaddr *)cliaddr, cliaddr_len);
}


ssize_t Readn(int fd, void *vptr, size_t n)
{
	size_t  nleft;
	ssize_t nread;
	char   *ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ( (nread = read(fd, ptr, nleft)) < 0) {
			if (errno == EINTR)
				nread = 0;
			else
				return -1;
		} else if (nread == 0)
			break;

		nleft -= nread;
		ptr += nread;
	}
	return n - nleft;
}

ssize_t Writen(int fd, const void *vptr, size_t n)
{
	size_t nleft;
	ssize_t nwritten;
	const char *ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;
			else
				return -1;
		}

		nleft -= nwritten;
		ptr += nwritten;
	}
	return n;
}
/////////////////////////////unit test////////////////////////////////////////
#if ((defined TEST_MYNET) || (defined TEST_UNIT))
#include <netinet/in.h> 
#include <ctype.h>
#define MAXLINE 80
int main()
{
	struct sockaddr_in cliaddr;
	socklen_t cliaddr_len;
	int listenfd, connfd;
	int ret,i;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];

	listenfd = my_create_tcp_server("127.0.0.1", 8000);
	if (listenfd < 0)
	{
		dbg("ERROR in my create tcp serer!\n");
		exit(EXIT_FAILURE);
	}

	cliaddr_len = sizeof(cliaddr);
	while (1) 
	{
		connfd = my_accept(listenfd, &cliaddr, &cliaddr_len);
		if (connfd < 0)
		{
			dbg("ERROR in my accpet\n");
			exit(EXIT_FAILURE);
		}
		ret = my_read(connfd, buf, MAXLINE);
		printf("received from %s at PORT %d\n",
				inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
				ntohs(cliaddr.sin_port));
				    
		for (i = 0; i < ret; i++)
			buf[i] = toupper(buf[i]);
		my_write(connfd, buf, ret);
		close(connfd);
	} 
}
#endif
/////////////////////////////unit test////////////////////////////////////////
